﻿//-----------------------------------------------------------------------
// <copyright file="DbFacade.cs" company="FER">
//     Copyright (c) All rights reserved.
// </copyright>
//-----------------------------------------------------------------------

namespace NMBP.DAL
{
    using System;
    using System.Collections.Generic;
    using System.Drawing;
    using System.IO;
    using System.Linq;
    using System.Reflection;

    using Db4objects.Db4o;
    using Db4objects.Db4o.Config;
    using NMBP.Models;

    /// <summary>
    /// Represents db facade towards db4o database
    /// </summary>
    public class DbFacade : IDbFacade
    {
        /// <summary>
        /// Path to db
        /// </summary>
        private string databasePath;

        /// <summary>
        /// Instance of database
        /// </summary>
        private IObjectContainer db;

        /// <summary>
        /// Initializes a new instance of the DbFacade class
        /// </summary>
        public DbFacade()
        {
            try
            {
                //this.databasePath = Directory.GetParent(
                //                    Directory.GetParent(
                //                    Directory.GetParent(
                //                    new FileInfo(Assembly.GetExecutingAssembly().Location).Directory.ToString()).ToString()).ToString()) + 
                //                    "\\NMBP.DAL\\DatabaseFile\\projectDb.db";

                this.databasePath = new FileInfo(Assembly.GetExecutingAssembly().Location).Directory + "\\DatabaseFile\\projectDb.db";
            }
            catch
            {
                this.db.Close();
            }
        }

        /// <summary>
        /// Initializes a new instance of the DbFacade class
        /// </summary>
        /// <param name="path">path to db facade</param>
        public DbFacade(string path)
        {
            try
            {
                this.databasePath = path;
            }
            catch
            {
                this.db.Close();
            }
        }

        #region Implementation of IDbFacade

        /// <summary>
        /// Gets person from database based on username and password
        /// </summary>
        /// <param name="username">person username</param>
        /// <param name="password">person password</param>
        /// <returns>list of persons</returns>
        public Person GetPerson(string username, string password)
        {
            this.db = Db4oEmbedded.OpenFile(this.databasePath);
            var users = this.db.Query<Person>(person => (person.Username == username && person.Password == password));
            var returnValue = users.ElementAtOrDefault(0);
            this.db.Close();

            return returnValue ?? null;
        }

        /// <summary>
        /// Gets all persons
        /// </summary>
        /// <returns>list of persons from db</returns>
        public List<Person> GetPersons()
        {
            this.db = Db4oEmbedded.OpenFile(this.databasePath);
            var persons = this.db.Query(typeof(Person));
            var returnList = persons.Cast<Person>().ToList();
            returnList.RemoveAll(x => x is Customer && ((Customer)x).IsInCart);
            this.db.Close();

            return returnList;
        }

        /// <summary>
        /// Saves person to database
        /// </summary>
        /// <param name="person">Person object</param>
        public void SavePerson(Person person)
        {
            this.db = Db4oEmbedded.OpenFile(this.databasePath);
            var personToDelete = this.db.Query<Person>(p => p.Username == person.Username).FirstOrDefault();
            if (personToDelete != null)
            {
                this.db.Delete(personToDelete);
            }

            if (person is Worker)
            {
                var depToDel =
                    this.db.Query<Department>(d => d.Name == ((Worker) person).Department.Name).FirstOrDefault();
                if (depToDel != null)
                {
                    this.db.Delete(depToDel);
                }
            }
            this.db.Store(person);
            this.db.Close();
        }

        /// <summary>
        /// Deletes person from db
        /// </summary>
        /// <param name="person">person to be deleted</param>
        public void DeletePerson(Person person)
        {
            this.db = Db4oEmbedded.OpenFile(this.databasePath);
            var personToDelete = this.db.Query<Person>(p => p.Username == person.Username).FirstOrDefault();
            this.db.Delete(personToDelete);
            this.db.Close();
        }

        /// <summary>
        /// Gets list of all workers
        /// </summary>
        /// <returns>list of workers</returns>
        public List<Worker> GetWorkers()
        {
            this.db = Db4oEmbedded.OpenFile(this.databasePath);

            this.db.Ext().Configure().ObjectClass(typeof(Worker)).CascadeOnUpdate(true);
            this.db.Ext().Configure().UpdateDepth(3);
            this.db.Ext().Configure().ObjectClass(typeof(Worker)).UpdateDepth(3);

            var persons = this.db.Query(typeof(Worker));
            var returnList = persons.Cast<Worker>().ToList();
            this.db.Close();

            return returnList;
        }

        /// <summary>
        /// Saves worker to database
        /// </summary>
        /// <param name="worker">Worker to be saved</param>
        public void SaveWorker(Worker worker)
        {
            this.db = Db4oEmbedded.OpenFile(this.databasePath);

            this.db.Ext().Configure().ObjectClass(typeof(Worker)).CascadeOnUpdate(true);
            this.db.Ext().Configure().UpdateDepth(2);
            this.db.Ext().Configure().ObjectClass(typeof(Worker)).UpdateDepth(2);

            var workersToDelete = this.db.Query<Worker>(c => c.Username == worker.Username).ToList();
            foreach(var workToDel in workersToDelete)
            {
                this.db.Delete(workToDel);
            }

            var deptToDelete = this.db.Query<Department>(d => d.Name == worker.Department.Name).FirstOrDefault();
            if (deptToDelete != null)
            {
                this.db.Delete(deptToDelete);
            }

            this.db.Store(worker);
            this.db.Close();
        }

        /// <summary>
        /// Gets list of all customers
        /// </summary>
        /// <returns>list of customers</returns>
        public List<Customer> GetCustomers()
        {
            this.db = Db4oEmbedded.OpenFile(this.databasePath);
            var persons = this.db.Query<Customer>(c => !c.IsInCart);
            var returnList = persons.Cast<Customer>().ToList();
            this.db.Close();

            return returnList;
        }

        /// <summary>
        /// Saves customer to database
        /// </summary>
        /// <param name="customer">customer to be saved</param>
        public void SaveCustomer(Customer customer)
        {
            this.db = Db4oEmbedded.OpenFile(this.databasePath);
            var customerToDelete = this.db.Query<Customer>(c => c.Username == customer.Username).FirstOrDefault();
            if (customerToDelete != null )
            {
                this.db.Delete(customerToDelete);
            }
            this.db.Store(customer);
            this.db.Close();
        }

        /// <summary>
        /// Get all products
        /// </summary>
        /// <returns>List of products</returns>
        public List<Product> GetProducts()
        {
            Db4oFactory.Configure().ObjectClass(typeof(Image)).Translate(new TSerializable());
            Db4oFactory.Configure().ObjectClass(typeof(Bitmap)).Translate(new TSerializable());

            this.db = Db4oEmbedded.OpenFile(this.databasePath);
            var persons = this.db.Query<Product>(product => product.InCart == false);
            var returnList = persons.Cast<Product>().ToList();
            this.db.Close();

            return returnList;
        }

        /// <summary>
        /// Saves one product
        /// </summary>
        /// <param name="product">product item</param>
        public void SaveProduct(Product product)
        {
            this.db = Db4oEmbedded.OpenFile(this.databasePath);
            this.db.Store(product);
            this.db.Close();
        }

        /// <summary>
        /// Saves list of products
        /// </summary>
        /// <param name="products">list of products to save</param>
        public void SaveProducts(List<Product> products)
        {
            this.db = Db4oEmbedded.OpenFile(this.databasePath);

            var oldProducts = this.db.Query<Product>(p => !p.InCart).ToList();
            foreach (var product in oldProducts)
            {
                this.db.Delete(product);
            }

            this.db.Store(products);
            this.db.Close();
        }

        /// <summary>
        /// Gets departments from database
        /// </summary>
        /// <returns>list of departments</returns>
        public List<Department> GetDepartments()
        {
            this.db = Db4oEmbedded.OpenFile(this.databasePath);
            this.db.Activate(typeof(Department), 3);
            var persons = this.db.Query(typeof(Department));
            var returnList = persons.Cast<Department>().ToList();
            this.db.Close();

            return returnList;
        }

        /// <summary>
        /// Save departments
        /// </summary>
        /// <param name="department">department to be saved</param>
        public void SaveDepartment(Department department)
        {
            this.db = Db4oEmbedded.OpenFile(this.databasePath);
            this.db.Activate(typeof(Department), 3);
            var departmentToDelete = this.db.Query<Department>(c => c.Name == department.Name).FirstOrDefault();
            if (departmentToDelete != null)
            {
                this.db.Delete(departmentToDelete);
            }
            this.db.Store(department);
            this.db.Close();
        }

        /// <summary>
        /// Saves departments to database
        /// </summary>
        /// <param name="departments">list of departments</param>
        public void SaveDepartments(List<Department> departments)
        {
            this.db = Db4oEmbedded.OpenFile(this.databasePath);

            var oldDepts = this.db.Query<Department>().ToList();
            foreach (var dept in oldDepts)
            {
                this.db.Delete(dept);
            }

            this.db.Store(departments);
            this.db.Close();
        }

        /// <summary>
        /// Gets all carts
        /// </summary>
        /// <returns>list of carts</returns>
        public List<Cart> GetCarts()
        {
            this.db = Db4oEmbedded.OpenFile(this.databasePath);
            var persons = this.db.Query(typeof(Cart));
            var returnList = persons.Cast<Cart>().ToList();
            this.db.Close();

            return returnList;
        }

        /// <summary>
        /// Gets customers unorderd cart if any
        /// </summary>
        /// <param name="customer">customer of cart</param>
        /// <returns>customers unordered cart or null</returns>
        public Cart GetCustomerUnorderedCart(Customer customer)
        {
            this.db = Db4oEmbedded.OpenFile(this.databasePath);
            var carts = this.db.Query<Cart>(cart => cart.Customer != null && cart.IsOrdered == false && cart.Customer.Username == customer.Username);
            var returnValue = carts.FirstOrDefault();
            this.db.Close();

            return returnValue;
        }

        /// <summary>
        /// Save cart to database
        /// </summary>
        /// <param name="cart">cart to be saved</param>
        public void SaveCart(Cart cart)
        {
            this.db = Db4oEmbedded.OpenFile(this.databasePath);
            var cartsToDelete = this.db.Query<Cart>(c => c.Customer != null && c.Customer.Username == cart.Customer.Username && c.IsOrdered == false);
            foreach (var cart2 in cartsToDelete)
            {
                this.db.Delete(cart2);
            }

            this.db.Store(cart);
            this.db.Close();
        }

        /// <summary>
        /// Gets orders from db
        /// </summary>
        /// <returns>list of orders</returns>
        public List<Order> GetPendingOrders()
        {
            this.db = Db4oEmbedded.OpenFile(this.databasePath);
            this.db.Activate(typeof(Order), 3);
            var orders = this.db.Query<Order>(order => !order.IsDone);
            var returnList = orders.ToList();
            this.db.Close();

            return returnList;
        }

        /// <summary>
        /// Gets orders from db
        /// </summary>
        /// <returns>list of orders</returns>
        public List<Order> GetOldOrders()
        {
            this.db = Db4oEmbedded.OpenFile(this.databasePath);
            this.db.Activate(typeof(Order), 3);
            var orders = this.db.Query<Order>(order => order.IsDone);
            var returnList = orders.ToList();
            this.db.Close();

            return returnList;
        }

        /// <summary>
        /// Saves order to db
        /// </summary>
        /// <param name="order">order to be saved</param>
        public void SaveOrder(Order order)
        {
            Db4oFactory.Configure().ObjectClass(typeof(Order)).CascadeOnDelete(true);
            this.db = Db4oEmbedded.OpenFile(this.databasePath);
            this.db.Activate(typeof(Order), 3);
            var orderToDelete = this.db.Query<Order>(o => o.IsDone == false && o.Code == order.Code).FirstOrDefault();
            if (orderToDelete != null)
            {
                this.db.Delete(orderToDelete);
            }

            var cartsToDelete = this.db.Query<Cart>(c => c.Customer != null && c.Customer.Username == order.Cart.Customer.Username && c.IsOrdered == false);
            foreach (var cart2 in cartsToDelete)
            {
                this.db.Delete(cart2);
            }

            this.db.Store(order);
            this.db.Close();
        }

        /// <summary>
        /// Gets all suppliers from db
        /// </summary>
        /// <returns>list of suppliers</returns>
        public List<Supplier> GetSuppliers()
        {
            this.db = Db4oEmbedded.OpenFile(this.databasePath);
            var suppliers = this.db.Query<Supplier>(s => s.IsActive = true);
            var returnList = suppliers.ToList();
            this.db.Close();

            return returnList;
        }

        /// <summary>
        /// Saves supplier to db
        /// </summary>
        /// <param name="supplier">supplier to be saved</param>
        public void SaveSupplier(Supplier supplier)
        {
            this.db = Db4oEmbedded.OpenFile(this.databasePath);

            var supplierToDelete = this.db.Query<Supplier>(s => s.Name == supplier.Name).FirstOrDefault();
            if (supplierToDelete != null)
            {
                this.db.Delete(supplierToDelete);
            }
            this.db.Store(supplier);
            this.db.Close();
        }

        /// <summary>
        /// Saves suppliers to db
        /// </summary>
        /// <param name="suppliers">suppliers to be saved</param>
        public void SaveSuppliers(List<Supplier> suppliers)
        {
            this.db = Db4oEmbedded.OpenFile(this.databasePath);

            var suppliersToDelete = this.db.Query<Supplier>().ToList();
            foreach(var supplier2 in suppliersToDelete)
            {
                this.db.Delete(supplier2);
            }
            this.db.Store(suppliers);
            this.db.Close();
        }

        #endregion
    }
}